package com.koushikdutta.ion;

import com.example.ohosasynclibrary.async.future.FutureCallback;
import com.example.ohosasynclibrary.async.util.FileCache;
import com.example.ohosasynclibrary.async.util.LogUtil;
import com.example.ohosasynclibrary.async.util.TextUtils;
import com.koushikdutta.ion.bitmap.BitmapInfo;
import com.koushikdutta.ion.gif.GifDecoder;
import com.koushikdutta.ion.gif.GifFrame;
import ohos.agp.components.Component;
import ohos.agp.components.Image;
import ohos.agp.components.element.Element;
import ohos.agp.components.element.PixelMapElement;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.render.PixelMapHolder;
import ohos.agp.utils.Color;
import ohos.agp.utils.Rect;
import ohos.agp.utils.RectFloat;
import ohos.global.resource.NotExistException;
import ohos.global.resource.Resource;
import ohos.media.image.PixelMap;

import java.io.IOException;
import java.lang.ref.WeakReference;

/**
 * Created by koush on 6/8/13.
 */
class IonDrawable extends PixelMapElement implements Component.DrawTask {
    private static final double LOG_2 = Math.log(2);
    private static final int TILE_DIM = 256;
    private static final long FADE_DURATION = 200;

    private Paint paint;
    private int alpha = 0xFF;
    private BitmapInfo info;
    private int placeholderResource;
    private Element placeholder;
    private int errorResource;
    private Element error;
    private Resource resources;
    private ResponseServedFrom servedFrom;
    private boolean fadeIn;
    private int resizeWidth;
    private int resizeHeight;
    private boolean repeatAnimation;
    private Ion ion;
    private BitmapFetcher bitmapFetcher;
    private IonDrawableCallback callback;
    private FutureCallback<IonDrawable> loadCallback;
    private IonGifDecoder gifDecoder;
    private Element bitmapDrawable;
    private int textureDim;
    private int maxLevel;
    private BitmapDrawableFactory bitmapDrawableFactory;

    private Element NULL_PLACEHOLDER;
    private Element NULL_BITMAPINFO;
    private Element NULL_ERROR;

    public FutureCallback<IonDrawable> getLoadCallback() {
        return loadCallback;
    }

    public IonDrawable setLoadCallback(FutureCallback<IonDrawable> loadCallback) {
        this.loadCallback = loadCallback;
        return this;
    }

    public IonDrawable ion(Ion ion) {
        if (ion == null)
            throw new AssertionError("null ion");
        this.ion = ion;
        return this;
    }

    public Element getCurrentDrawable() {
        if (info == null) {
            if (placeholderResource != 0) {
                try {
                    Resource resource = ion.getContext().getResourceManager().getResource(placeholderResource);
                    return new PixelMapElement(resource);
                } catch (IOException | NotExistException e) {
//                    e.printStackTrace();
                    LogUtil.error(FileCacheStore.class.getName(), e.getMessage());
                }
            }
        }
        if (info != null) {
            if (info.bitmap != null)
                return new PixelMapElement(resources);
            else if (info.gifDecoder != null) {
                GifFrame last = info.gifDecoder.getLastFrame();
                if (last != null)
                    return new PixelMapElement(last.image);
                if (placeholderResource != 0)
                    try {
                        Resource resource = ion.getContext().getResourceManager().getResource(placeholderResource);
                        return new PixelMapElement(resource);
                    } catch (IOException | NotExistException e) {
//                        e.printStackTrace();
                        LogUtil.error(FileCacheStore.class.getName(), e.getMessage());
                    }
                return null;
            }
        }
        if (errorResource != 0)
            try {
                Resource resource = ion.getContext().getResourceManager().getResource(errorResource);
                return new PixelMapElement(resource);
            } catch (IOException | NotExistException e) {
//                e.printStackTrace();
                LogUtil.error(FileCacheStore.class.getName(), e.getMessage());
            }
        return null;
    }

    public BitmapInfo getBitmapInfo() {
        return info;
    }

    // create an internal static class that can act as a callback.
    // dont let it hold strong references to anything.
    static class IonDrawableCallback implements FutureCallback<BitmapInfo> {
        private WeakReference<IonDrawable> ionDrawableRef;
        private String bitmapKey;
        private Ion ion;

        public IonDrawableCallback(IonDrawable drawable) {
            ionDrawableRef = new WeakReference<IonDrawable>(drawable);
        }

        public void register(Ion ion, String bitmapKey) {
            String previousKey = this.bitmapKey;
            Ion previousIon = this.ion;
            if (TextUtils.equals(previousKey, bitmapKey) && this.ion == ion)
                return;
            this.ion = ion;
            this.bitmapKey = bitmapKey;
            if (ion != null)
                ion.bitmapsPending.add(bitmapKey, this);
            unregister(previousIon, previousKey);
        }

        private void unregister(Ion ion, String key) {
            if (key == null)
                return;
            // unregister this drawable from the bitmaps that are
            // pending.

            // if this drawable was the only thing waiting for this bitmap,
            // then the removeItem call will return the TransformBitmap/LoadBitmap instance
            // that was providing the result.
            if (ion.bitmapsPending.removeItem(key, this)) {
                // find out who owns this thing, to see if it is a candidate for removal
                Object owner = ion.bitmapsPending.tag(key);
                if (owner instanceof TransformBitmap) {
                    TransformBitmap info = (TransformBitmap) owner;
                    ion.bitmapsPending.remove(info.key);
                    // this transform is also backed by a LoadBitmap* or a DeferredLoadBitmap, grab that
                    // if it is the only waiter
                    if (ion.bitmapsPending.removeItem(info.downloadKey, info))
                        owner = ion.bitmapsPending.tag(info.downloadKey);
                }
                // only cancel deferred loads... LoadBitmap means a download is already in progress.
                // due to view recycling, cancelling that may be bad, as it may be rerequested again
                // during the recycle process.
                if (owner instanceof DeferredLoadBitmap) {
                    DeferredLoadBitmap defer = (DeferredLoadBitmap) owner;
                    ion.bitmapsPending.remove(defer.key);
                }
            }

            ion.processDeferred();
        }

        @Override
        public void onCompleted(Exception e, BitmapInfo result) {
            // see if the imageview is still alive and cares about this result
            IonDrawable drawable = ionDrawableRef.get();
            if (drawable == null)
                return;
            FutureCallback<IonDrawable> callback = drawable.getLoadCallback();
            IonDrawable ionDrawable1 = drawable.ion(drawable.ion);
            IonDrawable ionDrawable = ionDrawable1.setBitmap(result, result.servedFrom).updateLayers();
            ionDrawable.callback = drawable.callback;
            ionDrawable.setBitmap(result);

            if (callback != null) {
                callback.onCompleted(e, ionDrawable);
            }
        }
    }

    class IonGifDecoder {
        GifDecoder gifDecoder;
        Exception exception;
        GifFrame currentFrame;
        long nextFrameRender;

        public IonGifDecoder(BitmapInfo info) {
            gifDecoder = info.gifDecoder.mutate();
            currentFrame = gifDecoder.getLastFrame();
        }

        Runnable loader = new Runnable() {
            @Override
            public void run() {
                try {
                    gifDecoder.nextFrame();
                } catch (OutOfMemoryError e) {
                    exception = new Exception(e);
                } catch (Exception e) {
                    exception = e;
                }
                Ion.mainHandler.postTask(postLoad);
            }
        };

        Runnable postLoad = new Runnable() {
            @Override
            public void run() {
                isLoading = false;
            }
        };

        long getDelay() {
            // error case?
            if (currentFrame == null)
                return 1000 / 10;
            long delay = currentFrame.delay;
            if (delay == 0)
                delay = 1000 / 10;
            return delay;
        }

        public GifFrame getCurrentFrame() {
            long now = System.currentTimeMillis();
            if (nextFrameRender == 0) {
                nextFrameRender = now + getDelay();
                scheduleNextFrame();
            }

            if (now >= nextFrameRender) {
                // see if a frame is available
                if (gifDecoder.getLastFrame() != currentFrame) {
                    // we have a frame waiting, grab it i guess.
                    currentFrame = gifDecoder.getLastFrame();
                    // check if we need to drop frames, or maintain timing
                    if (now > nextFrameRender + getDelay())
                        nextFrameRender = now + getDelay();
                    else
                        nextFrameRender += getDelay();
                }
                scheduleNextFrame();
            }

            return currentFrame;
        }

        boolean isLoading;

        public synchronized void scheduleNextFrame() {
            if (isLoading)
                return;
            if (exception != null)
                return;
            if (gifDecoder.getStatus() == GifDecoder.STATUS_FINISH && repeatAnimation)
                gifDecoder.restart();
            isLoading = true;
            Ion.getBitmapLoadExecutorService().execute(loader);
        }
    }

    public IonDrawable setFadeIn(boolean fadeIn) {
        this.fadeIn = fadeIn;
        return this;
    }

    public IonDrawable setBitmapFetcher(BitmapFetcher bitmapFetcher) {
        this.bitmapFetcher = bitmapFetcher;
//        if (ion == null)
//            throw new AssertionError("null ion");
        return this;
    }

    public IonDrawable setBitmapDrawableFactory(BitmapDrawableFactory factory) {
        this.bitmapDrawableFactory = factory;
        return this;
    }

    public void cancel() {
        if (callback != null) {
            callback.register(null, null);
        }
        bitmapFetcher = null;
    }

    public IonDrawable(BitmapInfo info) {
        super(info.bitmap);
    }

    public IonDrawable(Resource resources, Image image) {
        super(resources);

        image.addDrawTask(this);
        this.imageView = image;
        NULL_PLACEHOLDER = new PixelMapElement((PixelMap) null);
        NULL_BITMAPINFO = new PixelMapElement((PixelMap) null);
        NULL_ERROR = new PixelMapElement((PixelMap) null);

        this.resources = resources;
        paint = new Paint();
        callback = new IonDrawableCallback(this);
    }

    public IonDrawable updateLayers() {
        // always set up the placeholder, it will disappear automagically
        tryGetPlaceholderResource();
//        if (placeholder == null)
//            setDrawableByLayerId(0, NULL_PLACEHOLDER);
//        else
//            setDrawableByLayerId(0, placeholder);
//
//        if (info == null) {
//            setDrawableByLayerId(1, NULL_BITMAPINFO);
//            setDrawableByLayerId(2, NULL_ERROR);
//            return this;
//        }
//
//        // error case
//        if (info.bitmap == null && info.decoder == null && info.gifDecoder == null) {
//            setDrawableByLayerId(1, NULL_BITMAPINFO);
//            tryGetErrorResource();
//            if (error == null)
//                setDrawableByLayerId(2, NULL_ERROR);
//            else
//                setDrawableByLayerId(2, error);
//            return this;
//        }
//
//        if (info.gifDecoder == null) {
        // normal bitmap
        tryGetBitmapResource();
//            setDrawableByLayerId(1, bitmapDrawable);
//        }
//        else {
//            // gif or deepzoom
//            setDrawableByLayerId(1, NULL_BITMAPINFO);
//        }
//        setDrawableByLayerId(2, NULL_ERROR);

        return this;
    }

    public IonDrawable setBitmap(BitmapInfo info) {
        this.info = info;
        cancel();
        return this;
    }

    public IonDrawable setBitmap(BitmapInfo info, ResponseServedFrom servedFrom) {
        if (this.info == info)
            return this;

        cancel();
        this.servedFrom = servedFrom;
        this.info = info;
        gifDecoder = null;
        bitmapDrawable = null;
//        invalidateSelf();
        if (info == null)
            return this;

        if (info.decoder != null) {
            // find number of tiles across to fit
            double wlevel = (double) info.originalSize.getPointX() / TILE_DIM;
            double hlevel = (double) info.originalSize.getPointY() / TILE_DIM;

            // find the level: find how many power of 2 tiles are necessary
            // to fit the entire image. ie, fit it into a square.
            double level = Math.max(wlevel, hlevel);
            level = Math.log(level) / LOG_2;

            maxLevel = (int) Math.ceil(level);

            // now, we know the entire image will fit in a square image of
            // this dimension:
            textureDim = TILE_DIM << maxLevel;
        } else if (info.gifDecoder != null) {
            gifDecoder = new IonGifDecoder(info);
        }
        if (info.bitmap != null) {
            return new IonDrawable(info);
        }
        return this;
    }

    public IonDrawable setRepeatAnimation(boolean repeatAnimation) {
        this.repeatAnimation = repeatAnimation;
        return this;
    }

    public IonDrawable setSize(int resizeWidth, int resizeHeight) {
        if (this.resizeWidth == resizeWidth && this.resizeHeight == resizeHeight)
            return this;
        this.resizeWidth = resizeWidth;
        this.resizeHeight = resizeHeight;
//        invalidateSelf();

        return this;
    }

    public IonDrawable setError(int resource, Element drawable) {
        if ((drawable != null && drawable == error) || (resource != 0 && resource == errorResource))
            return this;

        errorResource = resource;
        error = drawable;
        return this;
    }

    public IonDrawable setPlaceholder(int resource, Element drawable) {
        if ((drawable != null && drawable == placeholder) || (resource != 0 && resource == placeholderResource))
            return this;

        placeholderResource = resource;
        placeholder = drawable;
        return this;
    }

    private Element tryGetErrorResource() {
        if (error != null)
            return error;
        if (errorResource == 0)
            return null;
        try {
            Resource resource = ion.getContext().getResourceManager().getResource(errorResource);
            error = new PixelMapElement(resource);
        } catch (IOException | NotExistException e) {
//            e.printStackTrace();
            LogUtil.error(FileCacheStore.class.getName(), e.getMessage());
        }

        return error;
    }

    private Element tryGetBitmapResource() {
        if (bitmapDrawable != null)
            return bitmapDrawable;
        if (info == null)
            return null;
        if (info.gifDecoder != null)
            return null;
        if (info.decoder != null)
            return null;
        if (info.bitmap == null)
            return null;
        bitmapDrawable = bitmapDrawableFactory.fromBitmap(info.bitmap);
        return bitmapDrawable;
    }

    private Element tryGetPlaceholderResource() {
        if (placeholder != null)
            return placeholder;
        if (placeholderResource == 0)
            return null;
        try {
            Resource resource = ion.getContext().getResourceManager().getResource(placeholderResource);
            placeholder = new PixelMapElement(resource);
        } catch (IOException | NotExistException e) {
//            e.printStackTrace();
            LogUtil.error(FileCacheStore.class.getName(), e.getMessage());
        }
        return placeholder;
    }

    private FutureCallback<BitmapInfo> tileCallback = new FutureCallback<BitmapInfo>() {
        @Override
        public void onCompleted(Exception e, BitmapInfo result) {

        }
    };

    @Override
    public void onDraw(Component component, Canvas canvas) {

        if (info == null) {
            // draw stuff
            super.drawToCanvas(canvas);

            // see if we can fetch a bitmap
            if (bitmapFetcher != null) {
                if (bitmapFetcher.sampleWidth == 0 && bitmapFetcher.sampleHeight == 0) {
                    if (component.getWidth() != 1)
                        bitmapFetcher.sampleWidth = component.getWidth();
                    if (component.getHeight() != 1)
                        bitmapFetcher.sampleHeight = component.getHeight();

                    // now that we have final dimensions, reattempt to find the image in the cache
                    bitmapFetcher.recomputeDecodeKey();
                    BitmapInfo found = ion.bitmapCache.get(bitmapFetcher.bitmapKey);
                    if (found != null) {
                        // won't be needing THIS anymore
                        bitmapFetcher = null;
                        // found what we're looking for, but can't draw at this very moment,
                        // since we need to trigger a new measure.
                        callback.onCompleted(null, found);
                        return;
                    }
                }

                // no image found fetch it.
                callback.register(ion, bitmapFetcher.bitmapKey);

                // check to see if there's too many imageview loads
                // already in progress
                if (BitmapFetcher.shouldDeferImageView(ion)) {
                    bitmapFetcher.defer();
                } else {
                    bitmapFetcher.execute();
                }
                // won't be needing THIS anymore
                bitmapFetcher = null;
            }

            // well, can't do anything else here.
            return;
        }

        if (info.decoder != null) {
            drawDeepZoom(component, canvas);
            return;
        }

        if (info.drawTime == 0)
            info.drawTime = System.currentTimeMillis();

        long destAlpha = this.alpha;

        if (fadeIn) {
            destAlpha = ((System.currentTimeMillis() - info.drawTime) << 8) / FADE_DURATION;
            destAlpha = Math.min(destAlpha, this.alpha);
        }

        // remove plaeholder if not visible
        if (destAlpha == this.alpha) {
            if (placeholder != null) {
                placeholder = null;
//                setDrawableByLayerId(0, NULL_PLACEHOLDER);
            }
        } /*else {
            // invalidate to fade in
//            if (placeholder != null)
//                invalidateSelf();
        }*/

        if (info.gifDecoder != null) {
            super.drawToCanvas(canvas);

            GifFrame frame = gifDecoder.getCurrentFrame();
            if (frame != null) {
                paint.setAlpha((int) destAlpha);
                PixelMapHolder pixelMapHolder = new PixelMapHolder(frame.image);
                canvas.drawPixelMapHolder(pixelMapHolder, getBounds().left, getBounds().top, paint);
                paint.setAlpha(this.alpha);
//                invalidateSelf();
            }
            return;
        }

        if (info.bitmap != null) {
            if (bitmapDrawable != null)
                bitmapDrawable.setAlpha((int) destAlpha);
        } else {
            if (error != null)
                error.setAlpha((int) destAlpha);
        }

        super.drawToCanvas(canvas);

        if (true)
            return;

        // stolen from picasso
        canvas.save();
        canvas.rotate(45, 0f, 0f);

        paint.setColor(Color.WHITE);
        RectFloat rectFloat = new RectFloat(0, -10, 7.5f, 10);
        canvas.drawRect(rectFloat, paint);

        Color sourceColor;
        if (servedFrom == ResponseServedFrom.LOADED_FROM_CACHE)
            sourceColor = Color.CYAN;
        else if (servedFrom == ResponseServedFrom.LOADED_FROM_CONDITIONAL_CACHE)
            sourceColor = Color.YELLOW;
        else if (servedFrom == ResponseServedFrom.LOADED_FROM_MEMORY)
            sourceColor = Color.GREEN;
        else
            sourceColor = Color.RED;

        paint.setColor(sourceColor);
        RectFloat rectFloat1 = new RectFloat(0, -9, 6.5f, 9);
        canvas.drawRect(rectFloat1, paint);

        canvas.restore();
    }

    private void drawDeepZoom(Component component, Canvas canvas) {
        // zoom 0: entire image fits in a TILE_DIMxTILE_DIM square

        // draw base bitmap for empty tiles
        // figure out zoom level
        // figure out which tiles need rendering
        // draw stuff that needs drawing
        // missing tile? fetch it
        // use parent level tiles for tiles that do not exist



        Rect clip = canvas.getLocalClipBounds();
        Rect bounds = getBounds();
        float clipWidth = clip.right - clip.left;
        float clipHeight = clip.bottom - clip.top;
        float boundWidth = bounds.right - bounds.left;
        float boundHeight = bounds.bottom - bounds.top;

        float zoom = (float) component.getWidth() / (float) clipWidth;

        float zoomWidth = zoom * boundWidth;
        float zoomHeight = zoom * boundHeight;

        double wlevel = Math.log(zoomWidth / TILE_DIM) / LOG_2;
        double hlevel = Math.log(zoomHeight / TILE_DIM) / LOG_2;
        double maxLevel = Math.max(wlevel, hlevel);

        int visibleLeft = Math.max(0, clip.left);
        int visibleRight = (int) Math.min(boundWidth, clipWidth);
        int visibleTop = Math.max(0, clip.top);
        int visibleBottom = (int) Math.min(boundHeight, clipHeight);
        int level = (int) Math.floor(maxLevel);
        level = Math.min(this.maxLevel, level);
        level = Math.max(level, 0);
        int levelTiles = 1 << level;
        int textureTileDim = textureDim / levelTiles;

        final boolean DEBUG_ZOOM = false;
        if (info.bitmap != null) {
            PixelMapHolder pixelMapHolder = new PixelMapHolder(info.bitmap);
            canvas.drawPixelMapHolder(pixelMapHolder, bounds.left, bounds.top, paint);
            if (DEBUG_ZOOM) {
                paint.setColor(Color.RED);
                paint.setAlpha(0x80);
                RectFloat rectFloat = new RectFloat(bounds.left, bounds.top, bounds.right, bounds.bottom);
                canvas.drawRect(rectFloat, paint);
                paint.setAlpha(0xFF);
            }
        } else {
            paint.setColor(Color.BLACK);
            RectFloat rectFloat = new RectFloat(bounds.left, bounds.top, bounds.right, bounds.bottom);
            canvas.drawRect(rectFloat, paint);
        }

        int sampleSize = 1;
        while (textureTileDim / sampleSize > TILE_DIM)
            sampleSize <<= 1;

        for (int y = 0; y < levelTiles; y++) {
            int top = textureTileDim * y;
            int bottom = textureTileDim * (y + 1);
            bottom = Math.min(bottom, bounds.bottom);

            if (bottom < visibleTop)
                continue;
            if (top > visibleBottom)
                break;
            for (int x = 0; x < levelTiles; x++) {
                int left = textureTileDim * x;
                int right = textureTileDim * (x + 1);
                right = Math.min(right, bounds.right);

                if (right < visibleLeft)
                    continue;
                if (left > visibleRight)
                    break;

                Rect texRect = new Rect(left, top, right, bottom);

                // find, render/fetch
//                    System.out.println("rendering: " + texRect + " for: " + bounds);
                String tileKey = FileCache.toKeyString(info.key, ",", level, ",", x, ",", y);
                BitmapInfo tile = ion.bitmapCache.get(tileKey);
                if (tile != null && tile.bitmap != null) {
                    // render it
//                        System.out.println("bitmap is: " + tile.bitmaps[0].getWidth() + "x" + tile.bitmaps[0].getHeight());
                    PixelMapHolder pixelMapHolder = new PixelMapHolder(tile.bitmap);
                    canvas.drawPixelMapHolder(pixelMapHolder, texRect.left, texRect.top, paint);
                    continue;
                }


                if (ion.bitmapsPending.tag(tileKey) == null) {
                    // fetch it
//                        System.out.println(info.key + ": fetching region: " + texRect + " sample size: " + sampleSize);

                    LoadBitmapRegion region = new LoadBitmapRegion(ion, tileKey, info.decoder, texRect, sampleSize);
                }
                ion.bitmapsPending.add(tileKey, tileCallback);

                int parentLeft = 0;
                int parentTop = 0;
                int parentUp = 1;
                int parentLevel = level - parentUp;
                if (x % 2 == 1)
                    parentLeft++;
                if (y % 2 == 1)
                    parentTop++;
                int parentX = x >> 1;
                int parentY = y >> 1;

                while (parentLevel >= 0) {
                    tileKey = FileCache.toKeyString(info.key, ",", parentLevel, ",", parentX, ",", parentY);
                    tile = ion.bitmapCache.get(tileKey);
                    if (tile != null && tile.bitmap != null)
                        break;
                    if (parentX % 2 == 1) {
                        parentLeft += 1 << parentUp;
                    }
                    if (parentY % 2 == 1) {
                        parentTop += 1 << parentUp;
                    }
                    parentLevel--;
                    parentUp++;
                    parentX >>= 1;
                    parentY >>= 1;
                }

                // well, i give up
                if (tile == null || tile.bitmap == null)
                    continue;


                int subLevelTiles = 1 << parentLevel;
                int subtileDim = textureDim / subLevelTiles;
                int subSampleSize = 1;
                while (subtileDim / subSampleSize > TILE_DIM)
                    subSampleSize <<= 1;
                int subTextureDim = subtileDim / subSampleSize;
//                    System.out.println(String.format("falling back for %s,%s,%s to %s,%s,%s: %s,%s (%s to %s)", x, y, level, parentX, parentY, parentLevel, parentLeft, parentTop, subTextureDim, subTextureDim >> parentUp));
                subTextureDim >>= parentUp;
                int sourceLeft = subTextureDim * parentLeft;
                int sourceTop = subTextureDim * parentTop;
                Rect sourceRect = new Rect(sourceLeft, sourceTop, sourceLeft + subTextureDim, sourceTop + subTextureDim);

                RectFloat sourceFloat = new RectFloat(sourceRect.left, sourceRect.top, sourceRect.right, sourceRect.bottom);
                RectFloat texFloat = new RectFloat(texRect.left, texRect.top, texRect.right, texRect.bottom);
                PixelMapHolder pixelMapHolder = new PixelMapHolder(tile.bitmap);
                canvas.drawPixelMapHolderRect(pixelMapHolder, sourceFloat, texFloat, paint);

                if (DEBUG_ZOOM) {
                    paint.setColor(Color.RED);
                    paint.setAlpha(0x80);
                    canvas.drawRect(texFloat, paint);
                    paint.setAlpha(0xFF);
                }
            }
        }
    }

    @Override
    public void setAlpha(int alpha) {
        super.setAlpha(alpha);
        this.alpha = alpha;
        paint.setAlpha(alpha);
    }

    public Image getImageView() {
        return imageView;
    }

    private Image imageView;

    static IonDrawable getOrCreateIonDrawable(Image imageView, int placeholderId) {
        Element current = imageView.getImageElement();
        IonDrawable ret = null;
        if (!(current instanceof IonDrawable)) {
            try {
                ret = new IonDrawable(imageView.getResourceManager().getResource(placeholderId), imageView);
            } catch (IOException | NotExistException e) {
//                e.printStackTrace();
                LogUtil.error(FileCacheStore.class.getName(), e.getMessage());
            }
        } else
            ret = (IonDrawable) current;
        // invalidate self doesn't seem to trigger the dimension check to be called by imageview.
        // are drawable dimensions supposed to be immutable?
        imageView.setImageElement(null);
        return ret;
    }
}